package org.zebra.web.advice;

import org.springframework.core.MethodParameter;
import org.springframework.core.annotation.Order;
import org.springframework.http.MediaType;
import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter;
import org.springframework.http.server.ServerHttpRequest;
import org.springframework.http.server.ServerHttpResponse;
import org.springframework.util.PathMatcher;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import org.zebra.common.core.BaseResponseBodyAdviceChain;
import org.zebra.common.core.R;
import org.zebra.common.core.Result;
import org.zebra.common.util.UriWhiteUtil;
import org.zebra.web.annotation.ExcludeResponse;

import javax.servlet.http.HttpServletRequest;
import java.lang.reflect.Method;
import java.util.*;

/**
 * 非空返回值的处理.
 *
 * @author zhanghongbin
 */
@ControllerAdvice
@Order(value = 1000)
public class NotVoidResponseBodyAdviceChain extends BaseResponseBodyAdviceChain {

    private Set<String> excludeUrls;
    private PathMatcher pathMatcher;

    public NotVoidResponseBodyAdviceChain(Set<String> excludeUrls, PathMatcher pathMatcher) {
        this.excludeUrls = excludeUrls;
        this.excludeUrls.addAll(UriWhiteUtil.getDefaultUriWhite());
        this.pathMatcher = pathMatcher;
    }

    /**
     * 只处理不返回void的，并且MappingJackson2HttpMessageConverter支持的类型.
     *
     * @param methodParameter 方法参数
     * @param clazz           处理器
     * @return 是否支持
     */
    @Override
    public boolean supports(MethodParameter methodParameter, Class<? extends HttpMessageConverter<?>> clazz) {
        HttpServletRequest request =
                ((ServletRequestAttributes) (RequestContextHolder.currentRequestAttributes())).getRequest();
        String uri = request.getServletPath();
        boolean flag = this.excludeUrls.stream().anyMatch(excludeUrl -> pathMatcher.matchStart(excludeUrl, uri));
        if (flag) return false;
        Method method = methodParameter.getMethod();
        // method为空、返回值为void、非JSON，直接跳过
        if (Objects.isNull(method)
                || method.getReturnType().equals(Void.TYPE)
                || !MappingJackson2HttpMessageConverter.class.isAssignableFrom(clazz)) {
            return false;
        }
        // 有ExcludeFromGracefulResponse注解修饰的，也跳过
        if (method.isAnnotationPresent(ExcludeResponse.class)) {
            return false;
        }
        if (this.baseResponseBodyAdviceChain != null) {
            return this.baseResponseBodyAdviceChain.supports(methodParameter, clazz);
        }
        return true;
    }

    @Override
    public Object beforeBodyWrite(
            Object body,
            MethodParameter methodParameter,
            MediaType mediaType,
            Class<? extends HttpMessageConverter<?>> clazz,
            ServerHttpRequest serverHttpRequest,
            ServerHttpResponse serverHttpResponse) {
        Result result;
        if (body == null) {
            result = R.ok();
        } else if (body instanceof Result) {
            result = (Result) body;
        } else {
            result = R.ok(body);
        }
        if (this.baseResponseBodyAdviceChain != null) {
            return this.baseResponseBodyAdviceChain.beforeBodyWrite(
                    result, methodParameter, mediaType, clazz, serverHttpRequest, serverHttpResponse);
        }
        return result;
    }
}
